﻿using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using LoD.Contracts;


namespace LoD.Tools.TilesetMaker {
    public class TileSet : INotifyPropertyChanged {
        #region TilesetType enum

        public enum TilesetType {
            Undefined = -1,
            Mobile = 1,
            Items = 2,
            Gumps = 3
        }

        #endregion

        public int FramesMax { get; private set; }
        public int FramesMin { get; private set; }
        private int _framesAmount;

        private int _tileSizeX, _tileSizeY;
        private TilesetType _type;

        public TileSet() {
            this.Separator = '-';
            this.FramesAmount = 8;
            this.Type = TilesetType.Undefined;
            this.Name = "NoName";

            this.AnimationNames = Enum.GetNames(typeof(Animation));
            this.DirectionNames = Enum.GetNames(typeof(Direction));

            this.AnimationCount = this.AnimationNames.Count();
            this.DirectionCount = this.DirectionNames.Count();

            this.Animations = new ObservableCollection<KeyValuePair<Animation>>();
            foreach (string name in this.AnimationNames) {
                this.Animations.Add(new KeyValuePair<Animation>
                { Key = (Animation)(Enum.Parse(typeof(Animation), name)), Value = name });
            }

            this.Directions = new ObservableCollection<KeyValuePair<Direction>>();
            foreach (string name in this.DirectionNames) {
                this.Directions.Add(new KeyValuePair<Direction>
                { Key = (Direction)(Enum.Parse(typeof(Direction), name)), Value = name });
            }
        }

        public string ExampleFilename {
            get {
                var rnd = new Random();
                var ext = new[] { "png", "bmp", "jpg", "gif" }.ElementAt(rnd.Next(3));
                switch (Type) {
                    case TilesetType.Mobile:
                        return string.Format(@"{1}{0}{2}{0}{3}{0}{4}.{5}", Separator, Name,
                                             Animations[rnd.Next(AnimationCount - 1)].Value,
                                             Directions[rnd.Next(DirectionCount - 1)].Value, rnd.Next(0, 7),
                                             ext);
                    case TilesetType.Items:
                    case TilesetType.Gumps:
                        return string.Format("someFilename{0}.{1}", rnd.Next(9), ext);
                    default:
                        return "n/a";
                }
            }
        }

        public TilesetType Type {
            get { return this._type; }
            set {
                this._type = value;
                switch (value) {
                    case TilesetType.Undefined:
                        this.TileSizeX = 0;
                        this.TileSizeY = 0;
                        this.FramesMax = 1;
                        this.FramesMin = 1;
                        this.FramesAmount = 1;
                        break;
                    case TilesetType.Mobile:
                        this.TileSizeX = 96;
                        this.TileSizeY = 96;
                        this.FramesMax = 12;
                        this.FramesMin = 1;
                        this.FramesAmount = 8;
                        break;
                    case TilesetType.Items:
                        this.TileSizeX = 64;
                        this.TileSizeY = 64;
                        this.FramesMax = 1;
                        this.FramesMin = 1;
                        this.FramesAmount = 1;
                        break;
                    case TilesetType.Gumps:
                        this.TileSizeX = 128;
                        this.TileSizeY = 128;
                        this.FramesMax = 1;
                        this.FramesMin = 1;
                        this.FramesAmount = 1;
                        break;
                    default:
                        throw new ArgumentOutOfRangeException("value");
                }
            }
        }

        public string TilesetSize {
            get { return string.Format("{0} x {1} Pixel", this.TilesetSizeX, this.TilesetSizeY); }
        }

        public int FramesAmount {
            get { return this._framesAmount; }
            set {
                if (value >= this.FramesMin && value <= this.FramesMax) this._framesAmount = value;
                this.TilesetSizeX = this.TileSizeX * value * this.DirectionCount;
            }
        }

        public string Name { get; set; }
        public int AnimationCount { get; private set; }
        public int DirectionCount { get; private set; }
        public ObservableCollection<KeyValuePair<Animation>> Animations { get; set; }
        public ObservableCollection<KeyValuePair<Direction>> Directions { get; set; }
        public string[] AnimationNames { get; private set; }
        public string[] DirectionNames { get; private set; }
        public char? Separator { get; set; }
        public int TilesetSizeX { get; private set; }
        public int TilesetSizeY { get; private set; }

        public int TileSizeX {
            get { return this._tileSizeX; }
            set {
                this._tileSizeX = Math.Abs(value);
                this.TilesetSizeX = this._tileSizeX * this.FramesAmount * this.DirectionCount;
            }
        }

        public int TileSizeY {
            get { return this._tileSizeY; }
            set {
                this._tileSizeY = Math.Abs(value);
                this.TilesetSizeY = this._tileSizeY * this.AnimationCount;
            }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        public Bitmap Create(string imageFolder) {
            var tileset = new Bitmap(this.TilesetSizeX, this.TilesetSizeY);

            foreach (var anim in this.Animations) {
                foreach (var dir in this.Directions) {
                    for (int f = 0; f < this.FramesAmount; f++) {
                        string filenameTemplate = string.Format(@"{1}{0}{2}{0}{3}{0}{4}", this.Separator, this.Name,
                                                                anim.Value, dir.Value, f);
                        string fileExt = string.Empty;
                        foreach (string ext in new[] { ".png", ".jpg", ".gif", ".bmp" }) {
                            if (!File.Exists(Path.Combine(imageFolder, string.Concat(filenameTemplate, ext)))) continue;
                            fileExt = ext;
                            break;
                        }
                        if (string.IsNullOrEmpty(fileExt)) continue;
                        string filename = Path.Combine(imageFolder, string.Concat(filenameTemplate, fileExt));
                        int offsetX = ((int)dir.Key * this._tileSizeX * this.FramesAmount) + (this._tileSizeX * f);
                        int offsetY = ((int)anim.Key * this._tileSizeY);
                        using (var currentFrame = new Bitmap(filename)) {
                            using (Graphics g = Graphics.FromImage(tileset)) {
                                g.DrawImage(currentFrame,
                                            new Rectangle(offsetX, offsetY, this._tileSizeX, this._tileSizeY));
                            }
                        }
                    }
                }
            }
            return tileset;
        }
    }

    public class KeyValuePair<T> : INotifyPropertyChanged {
        public T Key { get; set; }
        public string Value { get; set; }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }
}